#!/bin/bash
rpath="$(readlink ${BASH_SOURCE})"
if [ -z "$rpath" ];then
    rpath=${BASH_SOURCE}
fi
root="$(cd $(dirname $rpath) && pwd)"
cd "$root"
red=$(tput setaf 1)
green=$(tput setaf 2)
yellow=$(tput setaf 3)
blue=$(tput setaf 4)
cyan=$(tput setaf 5)
reset=$(tput sgr0)
runAsRoot(){
    cmd="$@"
    if [ -z "$cmd" ];then
        echo "${red}Need cmd${reset}"
        exit 1
    fi

    if (($EUID==0));then
        sh -c "$cmd"
    else
        if ! command -v sudo >/dev/null 2>&1;then
            echo "Need sudo cmd"
            exit 1
        fi
        sudo sh -c "$cmd"
    fi
}

user=${SUDO_USER:-$(whoami)}
home=$(eval echo ~$user)

db="$(cat db)"

mylog(){
    datetime=$(date +%FT%T)
    echo "$datetime $*"
}

usage(){
    cat<<-EOF
	Usage: $(basename $0) CMD

	CMD:
	    start       <id>
	    stop        <id>
	    restart     <id>
	    status      <id>
	    add
	    list
	    del         <id>
	    pac         <id>    "edit pac file"
	    updatePac
	    log         <id>
	    edit        <id>    [-n NAME] [-l LOCAL_PORT] [-u UPSTREAM] [-L LOGDIR] [-p ISPAC]
	    copy|cp     <id>    [newid]
	    em
	EOF
    if [ "$(uname)" == "Darwin" ];then
        cat<<-EOF

	    macProxy|mp <options>
	EOF
    fi

    exit 1
}

# the following line comes from install.sh
# sqlite3 "$db" "CREATE TABLE config(id integer primary key autoincrement,name varchar,local_port int unique,upstream varchar,logdir varchar,ispac int);"
add(){
    echo "Enter name local_port upstream [ispac:default 0] [logdir:default /tmp] [id]"
    read name local_port upstream ispac logdir id
    if [ -z $name ] || [ -z $local_port ] || [ -z "$upstream" ];then
        echo "${red}Input error.${reset}"
        exit 1
    fi
    if [ -z $logdir ];then
        logdir=/tmp
    fi
    if [ -z $ispac ];then
        ispac=0
    fi
    mylog "insert data..."
    if [ -z $id ];then
        sqlite3 "$db" "insert into config(name,local_port,upstream,logdir,ispac) values('$name',$local_port,'$upstream','$logdir',$ispac);" || { echo "${red}Add failed.${reset}"; exit 1; }
    else
        sqlite3 "$db" "insert into config(id,name,local_port,upstream,logdir,ispac) values($id,'$name',$local_port,'$upstream','$logdir',$ispac);" || { echo "${red}Add failed.${reset}"; exit 1; }
    fi
}
LSOF(){
    case $(uname) in
        Linux)
            if (($EUID!=0));then
                sudo lsof "$@"
            else
                lsof "$@"
            fi
            ;;
        Darwin)
            lsof "$@"
            ;;
    esac
}
list(){
    # echo -e ".header on\n.mode column\n.width 5 20 10\n select * from config;" | sqlite3 "$db"
    records="$(sqlite3 "$db" "select id,name,local_port,upstream,logdir,ispac from config;")"
    # echo "records: $records"
    local id name local_port upstream logdir ispac
    printf "%-5s %-15s ${green}%-12s${reset} %-18s %-12s %-5s\n" "id" "name" "local_port" "upstream" "logdir" "ispac"
    echo
    for r in $records;do
        IFS=$'|'
        read id name local_port upstream logdir ispac <<< "$r"
        if LSOF -iTCP -sTCP:LISTEN -P | grep -q "\<${local_port}\>";then
            printf "%-5s %-15s ${green}%-12s${reset} %-18s %-12s %-5s\n" "$id" "$name" "${local_port}" "$upstream" "$logdir" "$ispac"
        else
            printf "%-5s %-15s ${red}%-12s${reset} %-18s %-12s %-5s\n" "$id" "$name" "${local_port}" "$upstream" "$logdir" "$ispac"
        fi
    done
    
}

delete(){
    id=${1:?"${red}missing id${reset}"}
    NAME="$(bash name.sh $id)"
    if [ -z "$NAME" ];then
        exit 1
    fi
    stop $id
    rm runtime/$NAME.action 2>/dev/null
    rm runtime/$NAME.config 2>/dev/null
    rm runtime/$NAME.plist 2>/dev/null
    rm runtime/$NAME.service 2>/dev/null
    rm $home/Library/LaunchAgents/$NAME.plist 2>/dev/null

    sqlite3 "$db" "delete from config where id=$id;"
}

start(){
    id=${1:?"${red}missing id${reset}"}
    #tmux new-session -d -s "pac" privoxy --no-daemon --pidfile /tmp/pac.privoxy.pid $root/runtime/pac.config
    #tmux new-window -d -t "privoxy"  privoxy --no-daemon --pidfile /tmp/global.privoxy.pid $root/runtime/global.config
    #tmux new-session -d -s "global"  privoxy --no-daemon --pidfile /tmp/global.privoxy.pid $root/runtime/global.config

    #TODO 如果存在了怎么办;现在是每次开启都从新生成相关配置文件、服务文件
    bash createConfig.sh "$id"
    NAME="$(bash name.sh $id)"
    servicename="$NAME"

    case $(uname) in
        Linux)
            if (($EUID!=0));then
                sudo systemctl start $servicename.service
            else
                systemctl start $servicename.service
            fi
            ;;
        Darwin)
            if launchctl list | grep -q "\b$servicename\b";then
                echo "${red}service $servicename already loaded.${reset}"
                exit 1
            fi
            launchctl load -w $home/Library/LaunchAgents/$servicename.plist || echo "already start"
            # echo "Set system http and https proxy? [y/n]"
            # read ans
            # if [ "$ans" == 'y' ];then
                record="$(bash queryById.sh $id)"
                #must set IFS
                IFS=$'|'
                #must use "$record" instead of $record
                read unuse_name local_port upstream logdir ispac <<< "$record"
                echo "${green}clear mac proxy.${reset}"
                bash macProxy/setMacProxy.sh unset
                echo "set http proxy: ${local_port}"
                bash macProxy/setMacProxy.sh http ${local_port}
                echo "set https proxy: ${local_port}"
                bash macProxy/setMacProxy.sh https ${local_port}
            # fi
            ;;
    esac
}

stop(){
    id=${1:?"${red}missing id${reset}"}
    NAME="$(bash name.sh $id)"
    if [ -z "$NAME" ];then
        echo "No such service with id: $id"
        exit 1
    fi
    servicename="$NAME"
    #tmux kill-session -t "pac"
    #tmux kill-session -t "global"
    case $(uname) in
        Linux)
            if (($EUID!=0));then
                sudo systemctl stop $servicename
                sudo systemctl stop $servicename
            else
                systemctl stop $servicename
                systemctl stop $servicename
            fi
            ;;
        Darwin)
            launchctl unload -w $home/Library/LaunchAgents/$servicename.plist
            # echo "Clear system http and https proxy? [y/n]"
            # read ans
            # if [ "$ans" == 'y' ];then
                echo "${green}clear mac proxy.${reset}"
                bash macProxy/setMacProxy.sh unset
            # fi
            ;;
    esac
}

status(){
    id=${1:?"${red}missing id${reset}"}
    NAME="$(bash name.sh $id)"
    if [ -z "$NAME" ];then
        echo "No such service with id: $id"
        exit 1
    fi
    servicename="$NAME"
    case $(uname) in
        Linux)
            systemctl status $servicename.service
            ;;
        Darwin)
            launchctl list | grep $servicename
            ;;
    esac
}

pac(){
    # editor=vi
    # if command -v vim >/dev/null 2>&1;then
    #     editor=vim
    # fi

    # case $(uname) in
    #     Linux)
    #         STAT='stat'
    #         ;;
    #     Darwin)
    #         STAT='stat -x'
    #         ;;
    # esac
    # pacFile=template/pac.action
    # before="$($STAT $pacFile | grep Modify)" 

    # $editor template/pac.action

    # after="$($STAT $pacFile | grep Modify)" 

    # if [ "$before" != "$after" ];then
    #     echo "Please ${red}Restart${reset} manually."
    # else
    #     echo "pac file not changed."
    # fi

    editor=vi
    if command -v vim >/dev/null 2>&1;then
        editor=vim
    fi
    pacFile=template/pac.action
    # sha1sum "${pacFile}" > "${pacFile}.sha1"
    oldmd5sum="$(python md5.py ${pacFile})"
    $editor template/pac.action
    newmd5sum="$(python md5.py ${pacFile})"
    # if ! sha1sum -c --status "${pacFile}.sha1";then
    if [ "$newmd5sum" != "$oldmd5sum" ];then
        echo "${blue}Pac file changed.${reset}"
        restartPac
    else
        echo "${cyan}Pac file not changed.${reset}"
    fi
    # rm "${pacFile}.sha1"
}

restartPac(){
    ids="$(sqlite3 "$db" "select id from config where ispac=1;")"
    for id in $ids;do
        echo "restart: $id"
        stop $id
        start $id
    done
}

log(){
    id=${1:?"${red}missing id${reset}"}
    NAME="$(bash name.sh $id)"
    if [ -z "$NAME" ];then
        exit 1
    fi
    record="$(bash queryById.sh $id)"
    # must set IFS
    IFS=$'|'
    # must use "$record" instead of $record
    read name local_port upstream logdir ispac <<< "$record"
    logfile="$NAME.log"
    local lines=$(wc -l $logdir/$logfile | awk '{print $1}')
    local limitLines=20000
    if (($lines > $limitLines));then
        echo "*********************************Note************************************"
        echo "logfile:$logdir/$logfile greater than $limitLines lines."
        echo "*********************************Note************************************"
    fi
    tail -f $logdir/$logfile
}

edit(){
    id=${1:?"${red}missing id${reset}"}
    shift
    record="$(bash queryById.sh $id)"
    if [ -z $record ];then
        exit 1
    fi
    # must set IFS
    IFS=$'|'
    # must use "$record" instead of $record
    read name local_port upstream logdir ispac <<< "$record"
    changed=0
    while getopts ":n:l:u:L:p:" opt;do
        case $opt in
            n)
                if [ "$name" != $OPTARG ];then
                    changed=1
                    name=$OPTARG
                fi
                ;;
            l)
                if [ "$local_port" != $OPTARG ];then
                    changed=1
                    local_port=$OPTARG
                fi
                ;;
            u)
                if [ "$upstream" != $OPTARG ];then
                    changed=1
                    upstream=$OPTARG
                fi
                ;;
            L)
                if [ "$logdir" != $OPTARG ];then
                    changed=1
                    logdir=$OPTARG
                fi
                ;;
            p)
                if [ "$ispac" != $OPTARG ];then
                    changed=1
                    ispac=$OPTARG
                fi
                ;;
            \?)
                echo "Unkonw option \"$OPTARG\""
                exit 1
                ;;
            :)
                echo "Need arg for option: \"$OPTARG\""
                exit 1
                ;;
        esac
    done

    if [ $changed == 1 ];then
        sqlite3 "$db" "update config set name='$name',local_port=$local_port,upstream='$upstream',logdir='$logdir',ispac='$ispac' where id=$id;"
        echo "Restart $id"
        stop $id
        start $id
    else
        echo "No change."
    fi
}

copy(){
    id=${1:?"${red}missing id${reset}"}
    newid=${2}
    record="$(bash queryById.sh $id)"
    if [ -z $record ];then
        exit 1
    fi

    if [ -n "$newid" ];then
        sqlite3 "$db" "insert into config(id,name,upstream,logdir,ispac) select $newid,name,upstream,logdir,ispac from config where id=$id;"
    else
        sqlite3 "$db" "insert into config(name,upstream,logdir,ispac) select name,upstream,logdir,ispac from config where id=$id;"
    fi


}

updatePac(){
    # local timestamp=$(date +%FT%T)
    local localPac=template/pac.action
    local remotePac=/tmp/remotePac-${timestamp}
    local remotePacURL="https://source711.oss-cn-shanghai.aliyuncs.com/pac/pac.action"
    echo "Download remote pac file from: $remotePacURL to $remotePac"
    curl -L -o $remotePac $remotePacURL || { echo "Download error"; exit 1; }

    #1. get local pac header
    local localPacHeader=/tmp/pacHeader-${timestamp}
    sed -n -e '1,/{pac}/p' $localPac > $localPacHeader

    #2. get local pac body
    local localPacBody=/tmp/pacBody-${timestamp}
    sed -n -e '/{pac}/,$p' $localPac | sed -n -e '2,$p' > $localPacBody

    #3. get remote pac body
    local remotePacBody=/tmp/remotePacBody-${timestamp}
    sed -n -e '/{pac}/,$p' $remotePac | sed -n -e '2,$p' > $remotePacBody

    #4. merget localPacBody with remotePacBody
    cat $localPacHeader > $localPac
    cat $localPacBody $remotePacBody | sort | uniq | sed '/^$/d' >> $localPac
    echo "Please restart privoxy to apply new pac setting"

}

em(){
    editor=vi
    if command -v vim>/dev/null 2>&1;then
        editor=vim
    fi
    $editor ${BASH_SOURCE}
}

pacProxy(){
    upstream=${1}
    port=${2}
    bash macProxy/setMacProxy.sh pac $upstream $port
}

unsetPacProxy(){
    bash macProxy/setMacProxy.sh unset pac
}

socksProxy(){
    port="$1"
    bash macProxy/setMacProxy.sh socks "$port"
}

unsetSocksProxy(){
    bash macProxy/setMacProxy.sh unset socks
}

macProxy(){
    bash macProxy/setMacProxy.sh "$@"
}

cmd=$1
shift
case $cmd in
    add)
        add
        ;;
    list)
        list
        ;;
    del|delete|rm|remove)
        delete "$@"
        ;;
    start)
        start "$@"
        ;;
    restart)
        stop "$@"
        start "$@"
        ;;
    stop)
        stop "$@"
        ;;
    pac)
        pac
        ;;
    updatePac)
        updatePac "$@"
        ;;
    status)
        status "$@"
        ;;
    log)
        log "$@"
        ;;
    edit)
        edit "$@"
        ;;
    copy|cp)
        copy "$@"
        ;;
    em)
        em
        ;;
    macProxy|mp)
        macProxy "$@"
        ;;
    pacProxy)
        pacProxy "$@"
        ;;
    unsetPacProxy)
        unsetPacProxy
        ;;
    socksProxy)
        socksProxy "$@"
        ;;
    unsetSocksProxy)
        unsetSocksProxy
        ;;
    *)
        usage
        ;;
esac
